package com.vonzhou.spitter.persistence;

import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;

/**
 * @author XiaoDongLei
 * @Package com.vonzhou.springinaction.knights
 * @date 2018/4/1
 * @Description: 11.2　Spring与Java持久化API——--不使用Spring模板的纯JPA Repository
 * <p>
 * 1、在Spring中使用JPA的第一步是要在Spring应用上下文中将实体管理器工厂（entity manager factory）按照bean的形式来进行配置。
 * <p>
 * 2、@Transactional表明这个Repository中的持久化方法是在事务上下文中执行的。
 * <p>
 * 由于没有使用模板类来处理异常，所以我们需要为Repository添加@Repository注解，这样PersistenceExceptionTranslationPostProcessor就会知道要将这个bean产生的异常转换成Spring的统一数据访问异常。
 */
@Repository("spitterDao")
@Transactional
public class JpaSpitterDao implements SpitterDao {
	private static final String RECENT_SPITTLES =
			"SELECT s FROM Spittle s";
	private static final String ALL_SPITTERS =
			"SELECT s FROM Spitter s";
	private static final String SPITTER_FOR_USERNAME =
			"SELECT s FROM Spitter s WHERE s.username = :username";
	private static final String SPITTLES_BY_USERNAME =
			"SELECT s FROM Spittle s WHERE s.spitter.username = :username";

	//注入EntityManager
	/**
	 * 这里的真相是@PersistenceContext并不会真正注入EntityManager——至少，精确来讲不是这样的。
	 * <p>
	 * 它没有将真正的EntityManager设置给Repository，而是给了它一个EntityManager的代理。真正的EntityManager是与当前事务相关联的那一个，如果不存在这样的EntityManager的话，就会创建一个新的。这样的话，我们就能始终以线程安全的方式使用实体管理器。
	 * <p>
	 * <p>
	 * 还需要了解@PersistenceUnit和@PersistenceContext并不是Spring的注解，它们是由JPA规范提供的。为了让Spring理解这些注解，并注入EntityManager Factory或EntityManager，我们必须要配置Spring的Persistence-AnnotationBeanPostProcessor。如果你已经使用了<context:annotation-config>或<context:componentscan>，那么你就不必再担心了，因为这些配置元素会自动注册PersistenceAnnotationBeanPostProcessor bean。否则的话，我们需要显式地注册这个bean：
	 */
	@PersistenceContext
	private EntityManager em;

	@Override
	public void addSpitter(Spitter spitter) {
		//<co id="co_useEM"/>
		em.persist(spitter);
	}

	@Override
	public Spitter getSpitterById(long id) {
		//<co id="co_useEM"/>
		return em.find(Spitter.class, id);
	}

	@Override
	public void saveSpitter(Spitter spitter) {
		//<co id="co_useEM"/>
		em.merge(spitter);
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Spittle> getRecentSpittle() {
		return (List<Spittle>) em.createQuery(RECENT_SPITTLES).
				getResultList();
	}

	@Override
	public void saveSpittle(Spittle spittle) {
		em.persist(spittle);
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Spittle> getSpittlesForSpitter(
			Spitter spitter) {
		return (List<Spittle>) em.createQuery(SPITTLES_BY_USERNAME).
				setParameter("username", spitter.getUsername()).
				getResultList();
	}


	@Override
	public Spitter getSpitterByUsername(String username) {
		return (Spitter) em.createQuery(SPITTER_FOR_USERNAME).
				setParameter("username", username).
				getSingleResult();
	}

	@Override
	public void deleteSpittle(long id) {
		try {
			em.remove(getSpittleById(id));
		} catch (DataAccessException e) {
		}
	}

	@Override
	public Spittle getSpittleById(long id) {
		return em.find(Spittle.class, id);
	}

	@Override
	@SuppressWarnings("unchecked")
	public List<Spitter> findAllSpitters() {
		return em.createQuery(ALL_SPITTERS).getResultList();
	}
}